<?php
/**
 * This file is part of webman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author    walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link      http://www.workerman.net/
 * @license   http://www.opensource.org/licenses/mit-license.php MIT License
 */

namespace app\middleware;

use app\controller\wmapi\IndexController;
use App\Service\ConstantService;
use App\Service\RedisService;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\SignatureInvalidException;
use Tinywan\Jwt\Exception\JwtCacheTokenException;
use Tinywan\Jwt\Exception\JwtConfigException;
use Tinywan\Jwt\Exception\JwtTokenException;
use Tinywan\Jwt\Exception\JwtTokenExpiredException;
use Tinywan\Jwt\RedisHandler;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Facades\JWTAuth;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
use support\Db;
use Tinywan\Jwt\JwtToken;
use Predis;

/**
 * Class StaticFile
 * @package app\middleware
 */
class StaticFile implements MiddlewareInterface
{
    private const ACCESS_TOKEN = 1;
    public function process(Request $request, callable $next): Response
    {
        // Access to files beginning with. Is prohibited

        $head = $request->header();
        $action = $request->action; // 当前操作的方法
        $noAuthArr = ['login', 'index', 'captcha','refreshToken']; // 在这个数组内的 方法 验证
        if (!in_array($action, $noAuthArr)) {

            // 原来
            //                $checkjwt = JwtToken::verify(1, $head['authorization']);
            // 修改后  加了 try
//            [$type, $token] = explode(' ', $head['authorization']);
            $token = $head['authorization'];
//            IndexController::verifyTokens($token);
            try {
//                $checkjwt = JwtToken::verifyToken($token, 1);
                $tokenType = 1;


//                $checkjwt = JwtToken::verifyTokenDefine($token, 1);
                $checkjwt = self::verifyTokenDefine($token, 1);


            } catch (SignatureInvalidException $signatureInvalidException) {
                return IndexController::errored401($signatureInvalidException,'身份验证令牌无效');
            } catch (BeforeValidException $beforeValidException) {
                return IndexController::errored401($beforeValidException,'身份验证令牌尚未生效');
            } catch (ExpiredException $expiredException) {
                return IndexController::errored401($expiredException,'登录凭证过期，请重新登录');
            } catch (UnexpectedValueException $unexpectedValueException) {
                return IndexController::errored($unexpectedValueException,'获取的扩展字段不存在');
            } catch (JwtCacheTokenException | \Exception $exception) {
                return IndexController::errored($exception->getMessage(),'token验证错误');
            }

            $user = DB::table('wa_admins')->find($checkjwt['extend']['id']);
            $request->user_id = $checkjwt['extend']['id'];
            $request->user = $user;

            if ($user->status == 1){
                return IndexController::errored401('状态被禁用','状态被禁用，无法登录，请联系管理员');
            }

            if (empty($checkjwt) || empty($checkjwt['extend']['id'])) {
//                return response('<h1>500 登录验证失败</h1>', 500);
                return IndexController::errored($checkjwt,'登录验证失败');
            }
            if ($checkjwt['exp'] < time()){
//                return response('<h1>401 token 过期</h1>', 401);
                return IndexController::errored401($checkjwt,'登录凭证过期，请重新登录');
            }
            if ($user->last_login_time){
                if ($checkjwt['extend']['login_at'] != $user->last_login_time){
                    if ($user->id == 27){
                        var_dump($checkjwt['extend']['login_at']);
                        var_dump($user->last_login_time);
                    }

                    return IndexController::errored401('','您的账号已在其他地方登录，如非本人操作请修改密码');
                }
            }

        }



        if (strpos($request->path(), '/.') !== false) {
            return response('<h1>403 forbidden</h1>', 403);
        }
        /** @var Response $response */
        $response = $next($request);
        // Add cross domain HTTP header
        $response->withHeaders([
            'Access-Control-Allow-Credentials' => 'true',
            'Access-Control-Allow-Origin' => $request->header('origin', '*'),
            'Access-Control-Allow-Methods' => $request->header('access-control-request-method', '*'),
            'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', '*'),
        ]);
        return $response;
    }




    public static function verifyTokenDefine(string $token, int $tokenType): array
    {
        $config = self::_getConfig();
        $publicKey = self::ACCESS_TOKEN == $tokenType ? self::getPublicKey($config['algorithms']) : self::getPublicKey($config['algorithms'], self::REFRESH_TOKEN);
        JWT::$leeway = $config['leeway'];

        $decoded = JWT::decode($token, new Key($publicKey, $config['algorithms']));
        $decodeToken = json_decode(json_encode($decoded), true);
        if ($config['is_single_device']) {
            $cacheTokenPre = $config['cache_token_pre'];
            if ($tokenType == self::REFRESH_TOKEN) {
                $cacheTokenPre = $config['cache_refresh_token_pre'];
            }
            $client = $decodeToken['extend']['client'] ?? self::TOKEN_CLIENT_WEB;
            RedisHandler::verifyToken($cacheTokenPre, $client, (string)$decodeToken['extend']['id'], $token);
        }
        return $decodeToken;
    }


    /**
     * 来自 vendo/tinywan/jwt/src/jwtToken
     * @return array
     */
    private static function _getConfig(): array
    {
        $config = config('plugin.tinywan.jwt.app.jwt');
        if (empty($config)) {
            throw new JwtConfigException('jwt配置文件不存在');
        }
        return $config;
    }


    /**
     * 来自 vendo/tinywan/jwt/src/jwtToken
     * @desc: 根据签名算法获取【公钥】签名值
     * @param string $algorithm 算法
     * @param int $tokenType 类型
     * @return string
     * @throws JwtConfigException
     */
    private static function getPublicKey(string $algorithm, int $tokenType = self::ACCESS_TOKEN): string
    {
        $config = self::_getConfig();
        switch ($algorithm) {
            case 'HS256':
                $key = self::ACCESS_TOKEN == $tokenType ? $config['access_secret_key'] : $config['refresh_secret_key'];
                break;
            case 'RS512':
            case 'RS256':
                $key = self::ACCESS_TOKEN == $tokenType ? $config['access_public_key'] : $config['refresh_public_key'];
                break;
            default:
                $key = $config['access_secret_key'];
        }

        return $key;
    }

}
